<?php

declare ( strict_types = 1 )
	;

namespace Distributed\Group;

use Exception;
use Distributed\Constant;
use UntilDistributed\Log;
use Communication\TxSocketData;
use Distributed\App;
use Trance\Transaction;
use Trance\TransactionGroup;

/**
 * 事务操作
 */
class TransactionManage implements ITransactionManage {
	private static $obj = null;
	public static function getInstance() {
		if (is_null ( self::$obj )) {
			self::$obj = new TransactionManage ();
		}
		return self::$obj;
	}
	function __construct() {
	}
	
	/**
	 * 检测规则
	 * @param Transaction $trance        	
	 * @return bool
	 */
	private function checkRole(Transaction $trance): bool {
		return $trance->role == Constant::$txgroup_role_starter || $trance->propagation == Constant::$propagation_naver;
	}
	
	/**
	 * 预提交
	 * 
	 * @return [type] [description]
	 */
	public function precommit(TxSocketData $socketData, int $fd) {
		$groupId = $socketData->transGroup->groupId;
		$list = TransGroupManage::getInstance ()->getItemsByGroupId ( $groupId );
		if (empty ( $list )) {
			throw new Exception ( "have no groupinfo", 1 );
		}
		
		$data = [];
		
		foreach ( $list as $item ) {
			
			if ($this->checkRole ( $item )) {
				continue;
			}
			$hasPart = 1;
		
			if (App::$server->exist ( $item->fd )) {
				
// 				$data[] = $socketData;
				
				// 发送消息
				$ret = $this->sendMsg ( $item->fd, $socketData );
				if (! $ret) {
					
					Log::getInstance ()->error ( "txmanage: send precommit msg fail, fd: {$item->fd}" );
				} else {
					Log::getInstance ()->info ( "txmanage: send actor precommit" );
				}
			} else {
				// 记录补偿信息
				Log::getInstance ()->error ( "txmanage: precommit, error actor's  fd info, fd: {$item->fd}" );
			}
		}
	
// 		MessageProducer::publish('zblQueue', $data);
		// if(!$hasPart){
		// TransGroupManage::getInstance()->updateGroupStatus($groupId, Constant::$tx_status_precommit);
		// $socketData = TxSocketData::createResponse(Constant::$tx_complete_ok);
		// $strMsg = serialize($socketData) ."\r\n\r\n";
		// App::$server->send($fd, $strMsg);
		// return true;
		// }
		
		// //设置超时定时器
// 		$timer = swoole_timer_after ( $socketData->transGroup->waitTime * 1000, function () use ($groupId, $fd) {
// 			$status = TransGroupManage::getInstance ()->getGroupStatus ( $groupId );
// 			if ($status == Constant::$tx_status_precommit) {
// 				Log::getInstance ()->info ( 'other workers already response precommit status' );
// 			} else {
// 				// 事务组已经超时，response fail
// 				TransGroupManage::getInstance ()->updateGroupStatus ( $groupId, Constant::$tx_status_rollback );
// 				$socketData = TxSocketData::createResponse ( Constant::$tx_complete_fail );
// 				$strMsg = serialize ( $socketData ) . "\r\n\r\n";
// 				App::$server->send ( $fd, $strMsg );
// 			}
// 		} );
		
		return ;
	}
	
	/**
	 * 遍历提交事务参与者
	 * 
	 * @param [type] $transactionList
	 *        	[description]
	 * @return [type] [description]
	 */
	public function commit(string $groupId) {
		$socketData = new TxSocketData ();
		$socketData->action = Constant::SOCKET_ACTION_COMMIT;
		$socketData->transGroup = new TransactionGroup( $groupId );
		
		$list = TransGroupManage::getInstance ()->getItemsByGroupId ( $groupId );
		
// 		$data = [];
		
		/**
		 * 
		 * @var Transaction $item
		 */
		$item = null;
		
		$flag = 80;
		
	
		foreach ( $list as $item ) {
			if ($this->checkRole($item)) {
				continue;
			}
			
// 			if ($item->status !== Constant::SOCKET_ACTION_COMMIT) {
				
// 				$flag = 0;
				
// 				break;
// 			}
			
			if (App::$server->exist ( $item->fd )) {
				
				$socketData->transaction = $item;
// 				$data [] = $socketData;
				$ret = $this->sendMsg ( $item->fd, $socketData );
				if (! $ret) {
					Log::getInstance ()->error ( "txmanage: send commit msg fail, fd: {$item->fd}" );
				}
			} else {
				// 记录补偿信息
				Log::getInstance ()->error ( "txmanage: commit, error actor's  fd info, fd: {$item->fd}" );
			}
			TransGroupManage::getInstance ()->updateItemStatus ( $groupId, $item, Constant::$tx_status_commit );
		}
		
		
// 		if ($flag == 80) {
// 			MessageProducer::publish('zblQueue', $data);
			
// 			return ;
// 		}
		
// 		$rollBack = [];
		
		
// 		foreach ( $list as $item ) {
// 			if ($this->checkRole($item)) {
// 				continue;
// 			}
			
// 			$item->status = Constant::SOCKET_ACTION_ROLLBACK;
			
// 			$rollBack[] = $item;
// 		}
		
// 		MessageProducer::publish('zblQueue', $rollBack);
		
		
		// 保存事务状态
		TransGroupManage::getInstance ()->updateGroupStatus ( $groupId, Constant::$tx_status_commit );
		
	}
	
	/**
	 * 遍历参与者回滚
	 * 
	 * @param [type] $groupId
	 *        	[description]
	 * @param array $transactionList
	 *        	[description]
	 * @return [type] [description]
	 */
	public function rollback(string $groupId, array $transactionList = array()) {
		if (empty ( $transactionList )) {
			// 获取事务组参与者
			$list = TransGroupManage::getInstance ()->getItemsByGroupId ( $groupId );
		} else {
			$list = $transactionList;
		}
		
		$socketData = new TxSocketData ();
		
		
// 		$data = [];
		foreach ( $list as $item ) {
			if ($item->role == Constant::$txgroup_role_starter) {
				continue;
			}
			// 拿到连接并发送回滚消息
			if (App::$server->exist ( $item->fd )) {
				$socketData->fd = $item->fd;
				
				$socketData->transGroup = new TransactionGroup ( $groupId );
				$socketData->transaction = $item;
				$socketData->action = Constant::SOCKET_ACTION_ROLLBACK;
// 				$data[] = $socketData;
// 				// 发送消息
				$ret = $this->sendMsg ( $item->fd, $socketData );
				if (! $ret) {
					Log::getInstance ()->error ( "txmanage: send rollback msg fail, fd: {$item->fd}" );
				}
				
			} else {
				// fd无效
				Log::getInstance ()->error ( "txmanage: rollback, error actor's  fd info, fd: {$item->fd}" );
			}
			TransGroupManage::getInstance ()->updateItemStatus ( $groupId, $item, Constant::$tx_status_rollback );
		}
		
// 		dump($data);
		
// 		MessageProducer::publish('zblQueue', $data);
		
		
		// 保存事务状态
		TransGroupManage::getInstance ()->updateGroupStatus ( $groupId, Constant::$tx_status_rollback );
		
		return [];
	}
	
	/**
	 * [sendMsg description]
	 * 
	 * @param [type] $fd
	 *        	[description]
	 * @param [type] $socketData
	 *        	[description]
	 * @return [type] [description]
	 */
	public function sendMsg($fd, $socketData) {
		if (empty ( App::$server )) {
			return false;
		}
		
		$socketData->checkValid ();
		$strMsg = serialize ( $socketData ) . "\r\n\r\n";
		
		App::$server->send($fd, $strMsg);
		
		return true;
	}
}